package com.htudata.websocket.endpoint;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import com.htudata.a2service.A2Service;
import com.htudata.alarm.service.AlarmShowService;
import com.htudata.common.enums.*;
import com.htudata.common.service.TokenService;
import com.htudata.core.util.ResultGenerator;
import com.htudata.core.util.operation.CollectionsOperationUtils;
import com.htudata.core.util.operation.MapOperationUtils;
import com.htudata.core.util.operation.StringOperationUtils;
import com.htudata.core.util.operation.ThrowableOperationUtils;
import com.htudata.flow.service.FlowShowService;
import com.htudata.live.service.LiveService;
import com.htudata.transctFlow.service.TransctFlowShowService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Slf4j
@ServerEndpoint("/v1/websocket/{token}/{appCode}")
@Component
public class AgentServerEndpoint {

  private static final Map<String, AgentServerEndpoint> agentServerEndpoints = new ConcurrentHashMap<>();
  
  private static final AtomicInteger onlineCount = new AtomicInteger();
  
  private Session session;
  
  private String token;
  
  private String appCode;
  
  private String a2Info;
  
  private Map<String, JSONObject> interfaceInfos = new ConcurrentHashMap<>();
  @Autowired
  private  LiveService liveService;
  @Autowired
  private  AlarmShowService alarmShowService;
  @Autowired
  private  FlowShowService flowShowService;
  @Autowired
  private  TransctFlowShowService transctFlowShowService;
  @Autowired
  private  A2Service a2Service;
  
  @OnOpen
  public void onOpen(Session session, @PathParam("token") String token, @PathParam("appCode") String appCode)  {
    this.session = session;
    this.token = token;
    this.appCode = appCode;
    Map<String, List<String>> requestParameterMap = session.getRequestParameterMap();
    String a2 = null;
    if (CollectionsOperationUtils.isValidCollection(requestParameterMap.get("a2")))
      a2 = ((List<String>)requestParameterMap.get("a2")).get(0); 
    String a2Info = a2Service.validateToken(null, null, a2);
    if (a2Info == null) {
      try {
        JSONObject resultWithA2Status = ResultGenerator.genResultWithA2Status(ResultGenerator.genResult("0", null), A2CodeEnum.code_0.getCode());
        sendMessage(resultWithA2Status.toJSONString());
        session.close(new CloseReason((CloseReason.CloseCode)CloseReason.CloseCodes.CANNOT_ACCEPT, "Illegal token"));
      } catch (IOException e) {
        log.error("close session ocurre error: {} .", e.getMessage());
      } 
      return;
    } 
    this.a2Info = a2Info;
    agentServerEndpoints.put(this.session.getId(), this);
    addOnlineCount();
    log.info("有新窗口开始监听:" + session.getId() + ", appCode:" + this.appCode + ", 当前在线会话数为:" + getOnlineCount());
    if (PageSeqEnum.SEQ_001.getCode().equals(appCode)) {
      try {
        sendMessage(liveService.getDataTypes(false, ""));
        alarmShowService.sendAlarms();
        String getInternal3DFlowsTemp = flowShowService.getInternal3DFlowsTemp(a2Info);
        if (StringOperationUtils.isValidStr2(getInternal3DFlowsTemp))
          sendMessage(getInternal3DFlowsTemp);
        List<Integer> statusList = new ArrayList<>();
        statusList.add(Integer.valueOf(1));
        sendMessage(transctFlowShowService.getTransctFlows(false, statusList));
      }catch (Exception e) {
        String tmp= JSON.toJSONString(ResultGenerator.genErrorResult(RequestSeqEnum.SEQ_007.getCode(), RequestStatusEnum.STATUS_503
                .getCode(), ""));
        sendMessage(tmp);
      }
    } 
  }
  
  @OnClose
  public void onClose() {
    agentServerEndpoints.remove(this.session.getId());
    subOnlineCount();
    log.info("有一连接关闭！当前在线会话数为:" + getOnlineCount());
  }
  
  @OnError
  public void onError(Session session, Throwable error) {
    log.error("发生错误", error);
  }
  
  @OnMessage
  public void onMessage(String message, Session session) {
    log.info("收到来自窗口" + session.getId() + "的信息:" + message);
    if (isHeartMessage(message))
      return; 
    try {
      JSONObject json = JSONObject.parseObject(message);
      if (WSMsgEnum.SEQ_000.getCode().equals(json.getString("msgId"))) {
        JSONObject body = json.getJSONObject("body");
        this.interfaceInfos.put(body.getString("seq"), body);
        if (RequestSeqEnum.SEQ_004.getCode().equals(body.getString("seq")))
          try {
            String get3DFlowsTemp = flowShowService.get3DFlowsTemp(body.getString("flowId"), this.a2Info);
            if (StringOperationUtils.isValidStr2(get3DFlowsTemp))
              sendMessage(get3DFlowsTemp); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
        if (RequestSeqEnum.SEQ_006.getCode().equals(body.getString("seq")))
          try {
            String get3DFlowsTemp = transctFlowShowService.get3DFlowsTemp(body.getString("flowId"), this.a2Info);
            if (StringOperationUtils.isValidStr2(get3DFlowsTemp))
              sendMessage(get3DFlowsTemp); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
        if (RequestSeqEnum.SEQ_008.getCode().equals(body.getString("seq")))
          try {
            String flowHealthStatis = flowShowService.getFlowHealthStatis(body.getString("flowId"), body
                .getString("nodeId"), this.a2Info);
            if (StringOperationUtils.isValidStr2(flowHealthStatis))
              sendMessage(flowHealthStatis); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
        if (RequestSeqEnum.SEQ_009.getCode().equals(body.getString("seq")))
          try {
            String flowHealthRanking = flowShowService.getFlowHealthRanking(body.getString("flowId"), body
                .getString("nodeId"), body.getString("rankingType"), body.getIntValue("year"), body
                .getString("field"), body.getString("sort"), body.getIntValue("page"), body
                .getIntValue("perPage"), this.a2Info);
            if (StringOperationUtils.isValidStr2(flowHealthRanking))
              sendMessage(flowHealthRanking); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
        if (RequestSeqEnum.SEQ_010.getCode().equals(body.getString("seq")))
          try {
            String transctFlowHealthStatis = transctFlowShowService.getTransctFlowHealthStatis(body.getString("transctFlowId"), this.a2Info);
            if (StringOperationUtils.isValidStr2(transctFlowHealthStatis))
              sendMessage(transctFlowHealthStatis); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
        if (RequestSeqEnum.SEQ_011.getCode().equals(body.getString("seq")))
          try {
            String transctFlowHealthRanking = transctFlowShowService.getTransctFlowHealthRanking(body
                .getString("transctFlowId"), body.getString("rankingType"), body
                .getIntValue("year"), body.getString("field"), body.getString("sort"), body
                .getIntValue("page"), body.getIntValue("perPage"), this.a2Info);
            if (StringOperationUtils.isValidStr2(transctFlowHealthRanking))
              sendMessage(transctFlowHealthRanking); 
          } catch (Exception e) {
            log.error(ThrowableOperationUtils.getStackTraceStr(e));
          }  
      } 
    } catch (Exception e) {
      log.error("Parse response error occurred -> {}", e.getMessage());
    } 
  }
  
  public void sendMessage(String message) {
    try {
      if (StringOperationUtils.isValidStr2(message))
        this.session.getAsyncRemote().sendText(message); 
    } catch (Exception e) {
      log.error("send to application: {}.", message);
    } 
  }
  
  public static void sendAll(String json, Map<String, String> tgtAppCodeMap, JSONObject tgtInterfaceInfo) {
    AgentServerEndpoint agentServerEndpoint = null;
    for (Map.Entry<String, AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (MapOperationUtils.isValidMap(tgtAppCodeMap) &&
        !tgtAppCodeMap.containsKey(agentServerEndpoint.appCode))
        continue; 
      if (!MapOperationUtils.isValidMap((Map)tgtInterfaceInfo)) {
        agentServerEndpoint.sendMessage(json);
        continue;
      } 
      if (MapOperationUtils.isValidMap((Map)tgtInterfaceInfo) && 
        agentServerEndpoint.interfaceInfos.containsKey(tgtInterfaceInfo.getString("seq")) && (
        (JSONObject)agentServerEndpoint.interfaceInfos.get(tgtInterfaceInfo.getString("seq"))).get("flowId")
        .equals(tgtInterfaceInfo.getString("flowId")))
        agentServerEndpoint.sendMessage(json); 
    } 
  }
  
  public boolean isHeartMessage(String message) {
    try {
      JSONObject json = JSONObject.parseObject(message);
      String msgId = json.getString("msgId");
      if (msgId != null && msgId.equals(WSMsgEnum.SEQ_003.getCode()))
        return true; 
    } catch (Exception e) {
      log.error("Parse response error occurred -> {}", e.getMessage());
    } 
    return false;
  }
  
  public Map<String, List<Long>> getCurrentUserIds() {
    Map<String, List<Long>> result = new HashMap<>();
    AgentServerEndpoint agentServerEndpoint = null;
    String key = "userIds";
    long userId = -1L;
    List<Long> userIds = null;
    for (Map.Entry<String, AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (result.containsKey(key)) {
        ((List<Long>)result.get(key)).add(Long.valueOf(userId));
        continue;
      } 
      userIds = new ArrayList<>();
      userIds.add(Long.valueOf(userId));
      result.put(key, userIds);
    } 
    return result;
  }
  
  public void sendToALLUser(Map<String, String> map) {
    if (!MapOperationUtils.isValidMap(map))
      return; 
   AgentServerEndpoint agentServerEndpoint = null;
    long userId = -1L;
    for (Map.Entry<String,AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (!map.containsKey(userId + ""))
        continue; 
      if (StringOperationUtils.isValidStr2(map.get(userId + "")))
        agentServerEndpoint.sendMessage(map.get(userId + "")); 
    } 
  }
  
  public Map<String, List<Long>> getCurrentFlowsAndUserIds() {
    Map<String, List<Long>> result = new HashMap<>();
   AgentServerEndpoint agentServerEndpoint = null;
    String flowId = null;
    long userId = -1L;
    List<Long> userIds = null;
    for (Map.Entry<String, AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      if (!agentServerEndpoint.interfaceInfos.containsKey(RequestSeqEnum.SEQ_004.getCode()))
        continue; 
      flowId = ((JSONObject)agentServerEndpoint.interfaceInfos.get(RequestSeqEnum.SEQ_004.getCode())).getString("flowId");
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (result.containsKey(flowId)) {
        ((List<Long>)result.get(flowId)).add(Long.valueOf(userId));
        continue;
      } 
      userIds = new ArrayList<>();
      userIds.add(Long.valueOf(userId));
      result.put(flowId, userIds);
    } 
    return result;
  }
  
  public void sendFlows(Map<String, String> map) {
    if (!MapOperationUtils.isValidMap(map))
      return; 
   AgentServerEndpoint agentServerEndpoint = null;
    String flowId = null;
    long userId = -1L;
    for (Map.Entry<String,AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      if (!agentServerEndpoint.interfaceInfos.containsKey(RequestSeqEnum.SEQ_004.getCode()))
        continue; 
      flowId = ((JSONObject)agentServerEndpoint.interfaceInfos.get(RequestSeqEnum.SEQ_004.getCode())).getString("flowId");
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (!map.containsKey(flowId + "_" + userId))
        continue; 
      if (StringOperationUtils.isValidStr2(map.get(flowId + "_" + userId)))
        agentServerEndpoint.sendMessage(map.get(flowId + "_" + userId)); 
    } 
  }
  
  public Map<String, List<Long>> getCurrentTransctFlowsAndUserIds() {
    Map<String, List<Long>> result = new HashMap<>();
    AgentServerEndpoint agentServerEndpoint = null;
    String flowId = null;
    long userId = -1L;
    List<Long> userIds = null;
    for (Map.Entry<String, AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      if (!agentServerEndpoint.interfaceInfos.containsKey(RequestSeqEnum.SEQ_006.getCode()))
        continue; 
      flowId = ((JSONObject)agentServerEndpoint.interfaceInfos.get(RequestSeqEnum.SEQ_006.getCode())).getString("flowId");
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (result.containsKey(flowId)) {
        ((List<Long>)result.get(flowId)).add(Long.valueOf(userId));
        continue;
      } 
      userIds = new ArrayList<>();
      userIds.add(Long.valueOf(userId));
      result.put(flowId, userIds);
    } 
    return result;
  }
  
  public void sendTransctFlows(Map<String, String> map) {
    if (!MapOperationUtils.isValidMap(map))
      return; 
   AgentServerEndpoint agentServerEndpoint = null;
    String flowId = null;
    long userId = -1L;
    for (Map.Entry<String, AgentServerEndpoint> entry : agentServerEndpoints.entrySet()) {
      agentServerEndpoint = entry.getValue();
      if (!agentServerEndpoint.appCode.equals(PageSeqEnum.SEQ_001.getCode()))
        continue; 
      if (!agentServerEndpoint.interfaceInfos.containsKey(RequestSeqEnum.SEQ_006.getCode()))
        continue; 
      flowId = ((JSONObject)agentServerEndpoint.interfaceInfos.get(RequestSeqEnum.SEQ_006.getCode())).getString("flowId");
      userId = TokenService.getUserId(agentServerEndpoint.a2Info);
      if (!map.containsKey(flowId + "_" + userId))
        continue; 
      if (StringOperationUtils.isValidStr2(map.get(flowId + "_" + userId)))
        agentServerEndpoint.sendMessage(map.get(flowId + "_" + userId)); 
    } 
  }
  
  public Map<String, AgentServerEndpoint> getAgentServerEndpoints() {
    return agentServerEndpoints;
  }
  
  public Map<String, JSONObject> getInterfaceInfos() {
    return this.interfaceInfos;
  }
  
  public String getAppCode() {
    return this.appCode;
  }
  
  public String getA2Info() {
    return this.a2Info;
  }
  
  public int getOnlineCount() {
    return onlineCount.get();
  }
  
  private void addOnlineCount() {
    onlineCount.incrementAndGet();
  }
  
  private void subOnlineCount() {
    onlineCount.decrementAndGet();
  }

}
